using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Reflection;
using System.IO;
using Ltf.Remoting.Server;
using Ltf.Remoting.Settings;
using Ltf.Remoting.TeamFoundation;
using Ltf.Remoting.Operations;
using Microsoft.TeamFoundation.VersionControl.Client;
using Ltf.Remoting.TFAccess;
using Ltf.Remoting.Commands;
using System.Runtime.InteropServices;

namespace Ltf.Diffs
{
	public class Differ
	{
		public Diff2Results Diff2Results;
		public Diff3Results Diff3Results;
		public Diff2Document LeftDocument;
		public Diff2Document RightDocument;
		public Diff2Document CombinedDocument;

		public DiffDocument BaseLocalDocument;
		public DiffDocument BaseServerDocument;

		public Differ(string leftFileName, string rightFileName)
		{
			Diff2Results = new Diff2Results(CallGnuDiff(leftFileName, rightFileName));
			LeftDocument = new Diff2Document(leftFileName);
			RightDocument = new Diff2Document(rightFileName);
			CombinedDocument = new Diff2Document(rightFileName);
			LeftDocument.SetDiff(Diff2Results, DiffSide.Left);
			RightDocument.SetDiff(Diff2Results, DiffSide.Right);
			CombinedDocument.SetDiff(Diff2Results, DiffSide.Combined);

		}

		public Differ(string leftFileName, string rightFileName, bool ignoreAllWhiteSpace)
		{
			string switches = "-u0";
			if (ignoreAllWhiteSpace)
				switches += " -E -b -B -i";

			Diff2Results = new Diff2Results(CallGnuDiff(leftFileName, rightFileName, switches));
			LeftDocument = new Diff2Document(leftFileName);
			RightDocument = new Diff2Document(rightFileName);
			CombinedDocument = new Diff2Document(rightFileName);
			LeftDocument.SetDiff(Diff2Results, DiffSide.Left);
			RightDocument.SetDiff(Diff2Results, DiffSide.Right);
			CombinedDocument.SetDiff(Diff2Results, DiffSide.Combined);

		}

		public Differ(string localName, string baseName, string serverName, bool mergeView, bool commonBase)
		{
			Diff3Results d3r = new Diff3Results(CallGnuDiff3(localName, baseName, serverName));
			if (commonBase)
			{
				if (mergeView)
				{
					BaseServerDocument = new Ltf.Diffs.Diff3Docs.CommonBase.MergeViewLeft(serverName);
					BaseServerDocument.LoadResults(d3r);

					BaseLocalDocument = new Ltf.Diffs.Diff3Docs.CommonBase.MergeViewRight(localName);
					BaseLocalDocument.LoadResults(d3r);
				}
				else
				{
					BaseServerDocument = new Ltf.Diffs.Diff3Docs.CommonBase.FullViewLeft(serverName);
					BaseServerDocument.LoadResults(d3r);


					BaseLocalDocument = new Ltf.Diffs.Diff3Docs.CommonBase.FullViewRight(localName);
					BaseLocalDocument.LoadResults(d3r);
				}
			}
			else
			{
				{
					if (mergeView)
					{
						BaseServerDocument = new Ltf.Diffs.Diff3Docs.NoBase.MergeViewLeft(serverName);
						BaseServerDocument.LoadResults(d3r);

						BaseLocalDocument = new Ltf.Diffs.Diff3Docs.NoBase.MergeViewRight(localName);
						BaseLocalDocument.LoadResults(d3r);
					}
					else
					{
						BaseServerDocument = new Ltf.Diffs.Diff3Docs.NoBase.FullViewLeft(serverName);
						BaseServerDocument.LoadResults(d3r);


						BaseLocalDocument = new Ltf.Diffs.Diff3Docs.NoBase.FullViewRight(localName);
						BaseLocalDocument.LoadResults(d3r);
					}
				}
			}
			//RightDocument.SetDiff(Diff2Results, DiffSide.Right, 0, string.Empty);
			//CombinedDocument.SetDiff(Diff2Results, DiffSide.Combined, 0, string.Empty);
			Diff3Results = d3r;
		}


		public Differ(string blameFile)
		{
			int maxVersion = 0;
			string name;
			string targetFile = blameFile;
			if (blameFile.StartsWith("$"))
			{
				targetFile = LizardFileAccess.GetFilePath(null, blameFile);
				LizardFileAccess.GetNameAndVersionFromPath(blameFile, out name, out maxVersion);
				blameFile = name;
			}
			VersionControlServer vcs = ServerConnections.GetConnectionForPath(blameFile).VersionControlServer;
			Item item = vcs.GetItem(blameFile);
			ServerFunctions sf = new ServerFunctions();
			//TODO: check if these lines were needed. (running differ without lizard running?)
			//TFSettings tfs = (TFSettings)SettingsFactory.LoadSettings("Server", typeof(TFSettings));
			//ServerConnections.Connection = new TFServerConnection(tfs);
			List<LizardChangeset> historyRecords = sf.GetHistory(blameFile);
			LizardChangeset firstRecord = historyRecords[historyRecords.Count - 1];
			//	WorkspaceOperators wso = new WorkspaceOperators(ConnectionInstance.Connection.GetWorkspace(blameFile));
			string tmpPath = ServerConnections.DefaultConnection.Settings.TempPath;
			string fileName = Path.GetFileName(blameFile);
			int displayChangeset = maxVersion;
			if (maxVersion == 0)
				maxVersion = vcs.GetLatestChangesetId();
			//			string targetFile = Path.Combine(tmpPath, fileName);
			//			item.DownloadFile(targetFile);
			CombinedDocument = new Diff2Document(targetFile, firstRecord.ChangesetId, firstRecord.Commiter);
			int itemId = item.ItemId;
			foreach (LizardChangeset lcs in historyRecords)
			{
				//TODO: must change GetHistoy to return more meaningful results
				int changeSet = lcs.ChangesetId;
				if (changeSet < maxVersion)
				{
					string author = lcs.Commiter;
					Item hItem = vcs.GetItem(itemId, changeSet);
					if (hItem != null)
					{
						string sourceFile = Path.Combine(tmpPath, string.Format("{0}.{1}", fileName, changeSet.ToString()));
						hItem.DownloadFile(sourceFile);
						Diff2Results dr = new Diff2Results(CallGnuDiff(sourceFile, targetFile, "-d"));
						CombinedDocument.AddBlameResults(dr, displayChangeset, author);
						displayChangeset = changeSet;
						if (targetFile != blameFile)
							File.Delete(targetFile);
						targetFile = sourceFile;
					}
				}
			}
			if (targetFile != blameFile)
				File.Delete(targetFile);
		}

		public static Diff2Results CompareLines(string line1, string line2)
		{
			Nhunker nhunker = new Nhunker();

			List<Nhunker.Nhunk> nhunks = nhunker.GetNhunks(line1, line2);
			Diff2Results d2r = new Diff2Results(nhunks);
			return d2r;
		}

		private static string CallGnuDiff(string leftFileName, string rightFileName)
		{
			return CallGnuDiff(leftFileName, rightFileName, "-u0");
		}
		private static string CallGnuDiff(string leftFileName, string rightFileName, string switches)
		{

			//HACK: this is for encoding differences
			// if the 'text' of each file is the same, cheat!

			if (File.ReadAllText(leftFileName) == File.ReadAllText(rightFileName))
				return string.Empty;

			string args = "\"" + leftFileName + "\" \"" + rightFileName + "\" " + switches;
			Process diff = new Process();
			ProcessStartInfo psi = new ProcessStartInfo(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\gnu\\diff.exe", args);
			psi.RedirectStandardOutput = true;
			psi.RedirectStandardError = true;
			psi.UseShellExecute = false;
			diff.StartInfo = psi;
			diff.StartInfo.CreateNoWindow = true;
			string diffresults = string.Empty;
			try
			{
				diff.Start();
				diffresults = diff.StandardOutput.ReadToEnd();
				string errors = diff.StandardError.ReadToEnd();
			}
			catch
			{
			}
			return diffresults;
		}

		private static string CallGnuDiff3(string localName, string baseName, string serverName)
		{
			return CallGnuDiff3(localName, baseName, serverName, string.Empty);
		}
		private static string CallGnuDiff3(string localName, string baseName, string serverName, string switches)
		{
			string shortLocal = GetShortPath(localName);
			string shortBase = GetShortPath(baseName);
			string shortServer = GetShortPath(serverName);
			string args = "\"" + shortLocal + "\" \"" + shortBase + "\" \"" + shortServer + "\" " + switches;
			Process diff3 = new Process();
			ProcessStartInfo psi = new ProcessStartInfo(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\gnu\\bin\\diff3.exe", args);
			psi.RedirectStandardOutput = true;
			psi.RedirectStandardError = true;
			psi.UseShellExecute = false;
			diff3.StartInfo = psi;
			diff3.StartInfo.CreateNoWindow = true;
			string diff3results = string.Empty;
			try
			{
				diff3.Start();
				diff3results = diff3.StandardOutput.ReadToEnd();
				string errors = diff3.StandardError.ReadToEnd();
			}
			catch
			{
			}
			return diff3results;
		}

		[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
		private static extern int GetShortPathName([MarshalAs(UnmanagedType.LPTStr)] string path,
			[MarshalAs(UnmanagedType.LPTStr)] StringBuilder shortPath, int shortPathLength);

		private static string GetShortPath(string longPath)
		{
			StringBuilder shortPath = new StringBuilder(255);
			GetShortPathName(longPath, shortPath, shortPath.Capacity);
			return shortPath.ToString();
		}



	}
}
